#define __STDC_CONSTANT_MACROS
#include <opencv2/opencv.hpp>
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/core.hpp"
#include "opencv2/dnn.hpp"
#include "opencv2/imgcodecs/legacy/constants_c.h"
#include <iostream>
#include <string>
#include <fstream>
#include <sys/time.h>
#include "sample_nnie_api.h"
#include <unistd.h>
#include <stdlib.h>
#include <cstring>
#include "client.h"


extern "C"
{
int fb_init();
int fb_write(unsigned char *pShowScreen);
void fb_exit(int fd);

#include "libavcodec/avcodec.h"
#include "libavdevice/avdevice.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"
#include "libavutil/avutil.h"
#include "libavutil/time.h"
#include "libswscale/swscale.h"
#include "libavutil/pixdesc.h"
};

#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avdevice.lib")
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "avfilter.lib")
#pragma comment(lib, "swscale.lib")
#pragma comment(lib, "swresample.lib")
#pragma comment(lib, "postproc.lib")

cv::Mat avFrame2Mat(AVFrame* pAvFrame, AVCodecContext*pCodecCtx);

#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include "opencv2/imgproc/types_c.h"
#define BUF_SIZE 2592*1536*3
#define SHM_KEY 0x1234
using namespace std;
using namespace cv;

struct shmseg {
    int cnt;
    int complete;
    int width;
    int height;
    int channels;
    unsigned char buf[BUF_SIZE];
};


int errorFunc(int *IDTmp,int countTmp,int *StoreID,int countID)
{
	int Shift[countTmp] = {0};
	for(int i = 1; i<(countID+1); i++)
	{
		Shift[i] = StoreID[i-1];
	}
	float Prob[2] = {0};
	for(int i = 0; i<countTmp; i++)
	{
		Prob[0] += IDTmp[i] - StoreID[i];
	}
	for(int i = 0; i < countTmp; i++)
	{
		Prob[1] += IDTmp[i] - Shift[i];
	}

	return ((Prob[0] < Prob[1])? (Prob[0]/countTmp):(Prob[1]/countTmp));
}


int test_image_yuv(const char *filename, int width, int height, char* model, char* ipv4_addr, char* port)
//int test_image_yuv(Mat img, int width, int height, char* model)
{
    //FILE *imgyuv;
    struct timeval tv1,tv2;
    long t1, t2, time;
    unsigned char* buffer;
    //imgyuv = fopen(yuv420, "rb");
	
	//int size=width*height;
    //buffer=(unsigned char *)malloc(size*1.5*sizeof(unsigned char));

    //int model_load_flag=yolo_init("coco_yolov3_detect.wk");
    int model_load_flag=yolo_init(model);
    printf("model_load_flag : %d\n", model_load_flag);
    //int u32RetS = fread(buffer, size*1.5, 1, imgyuv);
    //memset(buffer, 0,size*3/2);
    //memcpy(buffer, imgyuv.data, size*3/2);
/*
    {
	AVCodec *pCodec; //解码器指针
	AVCodecContext* pCodecCtx; //ffmpeg解码类的类成员
	AVFrame* pAvFrame; //多媒体帧，保存解码后的数据帧
	AVFormatContext* pFormatCtx; //保存视频流的信息
	avdevice_register_all();
	av_register_all(); //注册库中所有可用的文件格式和编码器	
 	AVDictionary *optionsDict = NULL;
	
 	avformat_network_init();
	av_dict_set(&optionsDict, "rtsp_transport", "tcp", 0);                //采用tcp传输	,,如果不设置这个有些rtsp流就会卡着
	av_dict_set(&optionsDict, "stimeout", "2000000", 0);
	pFormatCtx = avformat_alloc_context();
	//if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0) { //检查文件头部
	if (avformat_open_input(&pFormatCtx, filename, 0, &optionsDict) != 0) { //检查文件头部
	//if (avformat_open_input(&pFormatCtx, "/dev/video0", inputFmt, NULL)   != 0) {
		printf("Can't find the stream!\n");
		return -1;
	}
	if (avformat_find_stream_info(pFormatCtx, NULL)<0) { //查找流信息
		printf("Can't find the stream information !\n");
		return -1;
	}
 
	int videoindex = -1;
	for (int i = 0; i < pFormatCtx->nb_streams; ++i) //遍历各个流，找到第一个视频流,并记录该流的编码信息
	{
		if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
			videoindex = i;
			break;
		}
	}
	if (videoindex == -1) {
		printf("Don't find a video stream !\n");
		return -1;
	}
	pCodecCtx = pFormatCtx->streams[videoindex]->codec; //得到一个指向视频流的上下文指针
	pCodec = avcodec_find_decoder(pCodecCtx->codec_id); //到该格式的解码器
	if (pCodec == NULL) {
		printf("Cant't find the decoder !\n"); //寻找解码器
		return -1;
	}
	if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { //打开解码器
		printf("Can't open the decoder !\n");
		return -1;
	}
 
	pAvFrame = av_frame_alloc(); //分配帧存储空间
	AVFrame* pFrameBGR = av_frame_alloc(); //存储解码后转换的RGB数据
 
 
 
	// 保存BGR，opencv中是按BGR来保存的
	int size = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);
	uint8_t *out_buffer = (uint8_t *)av_malloc(size);
	avpicture_fill((AVPicture *)pFrameBGR, out_buffer, AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);
 
	AVPacket* packet = (AVPacket*)malloc(sizeof(AVPacket));
	printf("-----------输出文件信息---------\n");
	av_dump_format(pFormatCtx, 0, filename, 0);
	printf("------------------------------");
	cout<<endl;
	struct SwsContext *img_convert_ctx;
	img_convert_ctx = sws_getContext(pCodecCtx->width,
		pCodecCtx->height, 
		pCodecCtx->pix_fmt, 
		pCodecCtx->width, 
		pCodecCtx->height,
		AV_PIX_FMT_BGR24, //设置sws_scale转换格式为BGR24，这样转换后可以直接用OpenCV显示图像了
		SWS_BICUBIC, 
		NULL, NULL, NULL);
 
	int ret;
	int got_picture;
	for (;;)
	{
		if (av_read_frame(pFormatCtx, packet) >= 0)
		{
			if (packet->stream_index == videoindex)
			{
				ret = avcodec_decode_video2(pCodecCtx, pAvFrame, &got_picture, packet);
				if (ret < 0)
				{
					printf("Decode Error.（解码错误）\n");
					return -1;
				}
				if (got_picture)
				{
					//YUV to RGB
					sws_scale(img_convert_ctx, 
						(const uint8_t* const*)pAvFrame->data, 
						pAvFrame->linesize, 
						0, 
						pCodecCtx->height,
						pFrameBGR->data, 
						pFrameBGR->linesize);
					//Mat mRGB(pCodecCtx->height, pCodecCtx->width, CV_8UC3, out_buffer);//（1）等效于下面
					//Mat mRGB(Size(pCodecCtx->width,pCodecCtx->height), CV_8UC3);//（2）
					//mRGB.data = out_buffer;//memcpy(pCvMat.data, out_buffer, size);
					
					Mat mRGB(Size(pCodecCtx->width, pCodecCtx->height), CV_8UC3);
					mRGB.data =(uchar*)pFrameBGR->data[0];//注意不能写为：(uchar*)pFrameBGR->data
					//test_image_yuv(mRGB, 416, 416, argv[2]);
					gettimeofday(&tv1, NULL);
					int yolo_run_flag=yolo_run(mRGB,width,height);
		//printf("yolo_run return flag : %d\n", yolo_run_flag);
        				gettimeofday(&tv2, NULL);
        				t1 = tv2.tv_sec - tv1.tv_sec;
        				t2 = tv2.tv_usec - tv1.tv_usec;
        				time = (long)(t1 * 1000 + t2 / 1000);
        				printf("yolo_run inference time : %dms\n", time);
				}
			}
			av_free_packet(packet);
		}
		else
		{
			break;
		}
	}
 
	av_free(out_buffer);
	av_free(pFrameBGR);
	av_free(pAvFrame);
	avcodec_close(pCodecCtx);
	avformat_close_input(&pFormatCtx);
	sws_freeContext(img_convert_ctx);
	cout<<"-------------Detection End-------------"<<endl;

        //usleep(300000);
    }
*/
	gettimeofday(&tv1, NULL);
        //int yolo_run_flag=yolo_run(buffer,width,height);
        //int yolo_run_flag=yolo_run(filepath,width,height);
	//Mat img = imread(filename);

        int shmid;
    	struct shmseg* shmp;
    	shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644 | IPC_CREAT); //创建共享内存段
    	if (shmid == -1) {
        perror("Shared memory");
        return 1;
    	}

    	//附加到段以获取指向它的指针，即将共享内存段附加到调用进程的地址空间
    	shmp = shmat(shmid, NULL, 0);
    	if (shmp == (void*)-1) {
        perror("Shared memory attach");
        return 1;
    	}
	

    	string strStore;
    	string strTmp;
	//cout << "strStore[0] = " << strStore[0] << endl;
	   	
		Mat yuvImage,img;
    	width=shmp->width;
		height=shmp->height;
    	yuvImage.create(height*3/2, width, CV_8UC1);

    	int StoreID[8];
    	int IDTmp[8];
    	int countID;
    	int countTmp;
    	bool flag = false;
    		//img=imread("1.jpg");
    		//cvtColor(img,yuvImage , CV_BGR2YUV_I420);
	while (1){//shmp->complete != 1) {			//cycle start
    	//{
    		//img=imread("1.jpg");
    		//printf("Size: %d,%d ",img.rows, img.cols);

        	/* 将数据块从共享内存传输到标准输出*/
        	//Size dSize=Size(416, 416);
    		//memcpy(yuvImage.data, shmp->buf, height*width*3/2);
    		//cvtColor(yuvImage, img, CV_YUV2BGR_I420);
    		//printf("Size: %d,%d ",img.rows, img.cols);
    		//resize(img,img,dSize, 0, 0, INTER_AREA);
    		//imwrite("sample_vio_picture.jpg",img);


		//for(int index=0;index<2;index++)
		{
			//img=imread("1.jpg");
			//cvtColor(yuvImage, img, CV_YUV2BGR_I420);
			/*if(index == 0)
				imwrite("./result/index0.jpg",img);
			if(index == 1)
				imwrite("./result/index1.jpg",img);*/
			/*after imread images,load yolo to test image*/
			//int yolo_run_flag=yolo_run(img,width,height,strStore,StoreID,countID);
			//cout << "strStore[" << index <<"] = " << strStore[index] << endl;
			int yolo_run_flag=yolo_run_yuv(shmp->buf,width,height,strStore,StoreID,countID);
			//cout << "strStore[" << index <<"] = " << strStore[index] << endl;
			cout << "strStore = " << strStore << endl;
			cout << "the coutID =\t" << countID << endl;
			for(int i = 0; i<countID; i++)
			{
				cout << "StoreID[" << i  << "] = " << StoreID[i] << '\t';
			}
			cout << endl;
			//printf("yolo_run return flag : %d\n", yolo_run_flag);
        	gettimeofday(&tv2, NULL);

        	t1 = tv2.tv_sec - tv1.tv_sec;
        	t2 = tv2.tv_usec - tv1.tv_usec;
        	time = (long)(t1 * 1000 + t2 / 1000);
        	tv1=tv2;
        	printf("yolo_run inference time : %dms\n", time);
        	cout << "\n\n\n\n" ;

			//Mat img1(height*3/2,width,CV_8UC1,shmp->buf);
			//cvtColor(img1,img1,CV_YUV2BGR_I420);
        	//imwrite("./result/index0.jpg",img1);
        	//resize(img1,img1,Size(800,480) );
        	//fb_write((unsigned char*) img1.ptr(0) );


			if(flag == false )
			{
				if(countID != 8)
				{
					client(ipv4_addr,port,"\0");
					strStore = "";
					for(int i = 0; i<countTmp; i++)
					{
						StoreID[i] = 0;
					}
					continue;
				}
				else
				{
					cout << "init ok !\n" ;
					flag = true;
					for(int i = 0; i< countID;i++)
					{
						IDTmp[i] =  StoreID[i];
					}
					countTmp = countID;
					strTmp = strStore;
				}

			}


			if(countID == countTmp)
			{
				for(int i = 0; i< countID;i++)
				{
					IDTmp[i] =  StoreID[i];
				}
				strTmp = strStore;
				cout << "complete new.send strStore:\t" << strStore << endl;
				client(ipv4_addr,port,strStore);
        	}
			else
			{
				if(errorFunc(IDTmp,countTmp,StoreID,countID) < 10)
				{
					cout << "error is small enough,send strTmp:\t" << strTmp << endl;
					client(ipv4_addr,port,strTmp);
				}
				else
				{
					cout << "complete different!\tsend null!" << endl;
					client(ipv4_addr,port," ");
				}
			}
        	/*if(index == 1)
        	{
        		if( !( (strStore[0].compare(strStore[1]) ) ))//&& (strStore[0].compare(strStore[2]))\
        				&& (strStore[0].compare(strStore[3])) && (strStore[0].compare(strStore[4])) ) )
        		{
        			cout << "do tcp!" << endl;
        			cout<<"will send plate:\t" << strStore[0] <<endl;
        			if(client(ipv4_addr,port,strStore[0]))
        			{
        				cout << "send message wrong!" << endl;
        			}
        			else
        			{
        				cout << "tcp send success!" << endl;
        			}
        		}
        		else
        		{
        			cout << "not do tcp" << endl;
        		}
        		//index = -1;
        		for(int i=0 ; i < 2 ;i++)
        		{
        			strStore[i]="";
        			//cout << "strStore\t" << strStore[i] << endl;
        		}
        		cout << "\n*****************************************************************\n" ;
        	}*/
    	//}
    		//free(buffer);
			strStore = "";
			for(int i = 0; i<countTmp; i++)
			{
				StoreID[i] = 0;
			}
			cout << "\n*****************************************************************\n" ;
		/*for(int i=0; i<5; i++)
		{
			cout << "strStore\t"<< i << " = " << strStore[i] << endl;
		} */

        	if (shmp->cnt == -1) {
            	    perror("read");
            	    return 1;
        	}
        	printf("Reading Process: Shared Memory: Read %d bytes\n", shmp->cnt);
        	//sleep(3);
		}
	}
    	yolo_unit();
    	printf("Reading Process: Reading Done, Detaching Shared Memory\n");
    	if (shmdt(shmp) == -1) {
            perror("shmdt");
            return 1;
    	}
    	printf("Reading Process: Complete\n");

    return 0;
}
void open_rtsp(const char *rtsp){
	unsigned int    i;
	int             ret;
	int             video_st_index = -1;
	int             audio_st_index = -1;
	AVFormatContext *ifmt_ctx = NULL;
	AVPacket        pkt;
	AVStream        *st = NULL;
	char            errbuf[64];
	AVDictionary *optionsDict = NULL;
	av_register_all();                                                          // Register all codecs and formats so that they can be used.
	avformat_network_init();                                                    // Initialization of network components
	av_dict_set(&optionsDict, "rtsp_transport", "tcp", 0);                //采用tcp传输	,,如果不设置这个有些rtsp流就会卡着
	av_dict_set(&optionsDict, "stimeout", "2000000", 0);                  //如果没有设置stimeout
 
	av_init_packet(&pkt);                                                       // initialize packet.
	pkt.data = NULL;
	pkt.size = 0;
	bool nRestart = false;
	AVStream *pVst;
	uint8_t* buffer_rgb = NULL;
	AVCodecContext *pVideoCodecCtx = NULL;
	AVFrame         *pFrame = av_frame_alloc();
	AVFrame         *pFrameRGB = av_frame_alloc();
	int got_picture;
	SwsContext      *img_convert_ctx = NULL;
	AVCodec *pVideoCodec = NULL;
 
	if ((ret = avformat_open_input(&ifmt_ctx, rtsp, 0, &optionsDict)) < 0) {            // Open the input file for reading.
		printf("Could not open input file '%s' (error '%s')\n", rtsp, av_make_error_string(errbuf, sizeof(errbuf), ret));
		goto EXIT;
	}
EXIT:
 
	if (NULL != ifmt_ctx) {
		avformat_close_input(&ifmt_ctx);
		ifmt_ctx = NULL;
	}
 
	return;
}
int main(int argc, char** argv)
{
	//argv[3] ip_addr argv[4] port
    //const char *image_yuv_demo = argv[1];
/*   
    VideoCapture video("rtsp://admin:admin@10.193.232.4:554/cam/realmonitor?channel=1&subtype=1");
    Mat img;
    if (!video.read(img) ) {
               cout<<"falied to read img!"<<endl;
                    return -1;
    }
    cout<<"imgRow:"<<img.rows<<endl;
      //int flag=test_image_yuv( image_yuv_demo,416,416, argv[2]);
*/

	//int fb = fb_init();
	cout << "going to do with shell!" << endl;
	//system("pwd");
	//system("cd vi_test");
	//system("pwd");
	system("vio_test_1 100000 &");
	sleep(3);
	//system("cd -");

	cout << "assert I am going to test\n\n" ;
	char* filename = argv[1];
	int flag=test_image_yuv( filename,416,416, argv[2],argv[3],argv[4]);
        return 0;
}

